<template>
	<view>
		<canvas
			type="2d"
			id="canvas"
			:style="{
				width: canvaswidth + 'px',
				height: (canvaswidth / 375) * 258 + 'px'
			}"
		></canvas>
	</view>
</template>
<script>
import EGC from "./EGC.js";
class ChainContext2d {
	constructor(canvas) {
		this.ctx = canvas.getContext("2d");
	}

	lineWidth(val) {
		this.ctx.lineWidth = val;
		return this;
	}
	strokeStyle(val) {
		this.ctx.strokeStyle = val;
		return this;
	}
	font(val) {
		this.ctx.font = val;
		return this;
	}
	textAlign(val) {
		this.ctx.textAlign = val;
		return this;
	}

	beginPath() {
		this.ctx.beginPath();
		return this;
	}
	closePath() {
		this.ctx.closePath();
		return this;
	}
	moveTo(x, y) {
		this.ctx.moveTo(x, y);
		return this;
	}
	lineTo(x, y) {
		this.ctx.lineTo(x, y);
		return this;
	}
	fillText(text, x, y, maxWidth) {
		this.ctx.strokeText(text, x, y, maxWidth);
		return this;
	}
	stroke() {
		this.ctx.stroke();
		return this;
	}
	clearRect(w, h) {
		this.ctx.clearRect(0, 0, w, h);
	}
	quadraticCurveTo(x1, y1, x2, y2) {
		this.ctx.quadraticCurveTo(x1, y1, x2, y2);
		return this;
	}
	bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2x, p2y) {
		this.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2x, p2y);
		return this;
	}
	translate(x,y){
		this.ctx.translate(x,y);
		return this;
	}
	scale(scaleX,scaleY){
		this.ctx.scale(scaleX,scaleY);
		return this;
	}
}
const getDistance = (p1, p2) => {
	return Math.sqrt(Math.pow(p1.x + p2.x, 2) + Math.pow(p1.y + p1.y, 2));
};

const getControlpoint = (points) => {
	//三个点出两个控制点
	const p01 = getDistance(points[0], points[1]);
	const p12 = getDistance(points[1], points[2]);
	const p02 = p01 + p12;
	let vector = [points[2].x - points[0].x, points[2].y - points[0].y];
	return [
		{
			x: points[1].x - (vector[0] * 0.5 * p01) / p02,
			y: points[1].y - (vector[1] * 0.5 * p01) / p02
		},
		{
			x: points[1].x + (vector[0] * 0.5 * p01) / p02,
			y: points[1].y + (vector[1] * 0.5 * p01) / p02
		}
	];
};
let multiple = 8;
let offsetx = 200;
let offsety = 200;
let bigGridX = 3;
let bigGridY = 2;
let direction = "x";
let SMALL_INTERVAL = 10 * multiple;
let textData = [];
export default {
	props: {
		ecgArr: {
			type: Array,
			default: () => []
		},
		pageNum: {
			type: Number,
			default: 0
		}
	},
	watch: {
		ecgArr(val, oldVal) {
			if (val.length > 0) {
				textData = val;
			}
		}
	},
	data() {
		return {
			ecg: null,
			dpx: 2,
			canvas: "",
			ctx: "",
			canvaswidth: uni.upx2px(750)
		};
	},
	mounted() {
		let query = this.createSelectorQuery(); // 存一个 SelectorQuery 对象实例
		query
			.select("#canvas") // 在当前页面下选择 canvas 的节点，返回一个 NodesRef 对象实例，可以用于获取节点信息。
			.fields({
				// 获取节点的相关信息
				node: true, // 是否返回节点对应的 Node 实例
				size: true // 是否返回节点尺寸
			});

		// #ifdef H5
		query = document.getElementById("canvas");
		// #endif
		// uni.showLoading({
		// 	title: "加载数据中"
		// });
		query.exec((res) => {
			this.canvas = res[0].node;
			this.ctx = new ChainContext2d(this.canvas);
			this.bigGridX = bigGridX;
			this.bigGridY = bigGridY;
			this.direction = direction;
			this.canvas.width = bigGridX * SMALL_INTERVAL * 25 + offsetx * 2;
			this.canvas.height = bigGridY * SMALL_INTERVAL * 25 + offsety * 2;
			// this.ctx.scale(1, 1);
			this.ctx.translate(0.5, 0.5);
			this.grid(0, 1, []);
		});
		/**
		 * 功能：实例化 ECG 类
		 * @author LOVEtwelve
		 * @date 2023-03-09
		 * @param {Object}
		 * @argument {SelectorQuery} query Canvas的DOM对象
		 * @argument {Number} bigGridX 最大号格子在X轴方向上的个数
		 * @argument {Number} bigGridY 最大号格子在Y轴方向上的个数
		 * @argument {Number} dpx H5中需除以2
		 * @argument {String} direction 心电图方向 x为横向 y为竖向
		 * @argument {Number} index 若 index 为 0 则会绘制标注图案
		 */
		setInterval(() => {
			this.update(this.ecgArr);
		}, 40);
	},
	methods: {
		update(data) {
			this.grid(0, 1, data);
		},
		//绘制格子
		grid(index, dpx, data) {
			// console.log("这里到了吗", data, this);
			if (!this.ctx) {
				return;
			}
			this.ctx.clearRect(this.canvas.width, this.canvas.height);
			const d = dpx ? dpx : 1;
			/* 1 x 1 区域分割线 start */
			for (let i = 0; i <= 25 * this.bigGridX; i++) {
				this.ctx
					.beginPath()
					.strokeStyle("#FAD2F0")
					.lineWidth((multiple * 1) / d)
					.moveTo((i * SMALL_INTERVAL) / d + offsetx, offsety)
					.lineTo((i * SMALL_INTERVAL) / d + offsetx, this.canvas.height - offsety)
					.stroke()
					.closePath();
			}
			for (let i = 0; i <= 25 * this.bigGridY; i++) {
				this.ctx
					.beginPath()
					.strokeStyle("#FAD2F0")
					.lineWidth((multiple * 1) / d)
					.moveTo(offsetx, (i * SMALL_INTERVAL) / d + offsety)
					.lineTo(this.canvas.width - offsetx, (i * SMALL_INTERVAL) / d + offsety)
					.stroke()
					.closePath();
			}
			/* 1 x 1 区域分割线 end */

			/* 5 x 5 区域分割线 start */
			for (let i = 0, len = 25 * this.bigGridX; i <= len; i += 5) {
				this.ctx
					.beginPath()
					.strokeStyle("#FFC0CB")
					.lineWidth((2 * multiple) / d)
					.moveTo((i * SMALL_INTERVAL) / d + offsetx, offsety)
					.lineTo((i * SMALL_INTERVAL) / d + offsetx, this.canvas.height - offsety)
					.stroke()
					.closePath();
			}
			for (let i = 0, len = 25 * this.bigGridY; i <= len; i += 5) {
				this.ctx
					.beginPath()
					.strokeStyle("#FFC0CB")
					.lineWidth((2 * multiple) / d)
					.moveTo(offsetx, (i * SMALL_INTERVAL) / d + offsety)
					.lineTo(this.canvas.width - offsetx, (i * SMALL_INTERVAL) / d + offsety)
					.stroke()
					.closePath();
			}
			/* 5 x 5 区域分割线 end */

			/* 25 x 25 区域分割线 start */
			for (let i = 0, len = 25 * this.bigGridX; i <= len; i += 25) {
				this.ctx
					.beginPath()
					.strokeStyle("#FF7A7A")
					.lineWidth((3 * multiple) / d)
					.moveTo((i * SMALL_INTERVAL) / d + offsetx, offsety)
					.lineTo((i * SMALL_INTERVAL) / d + offsetx, this.canvas.height - offsety)
					.stroke()
					.closePath();
			}
			for (let i = 0, len = 25 * this.bigGridY; i <= len; i += 25) {
				this.ctx
					.beginPath()
					.strokeStyle("#FF7A7A")
					.lineWidth((3 * multiple) / d)
					.moveTo(offsetx, (i * SMALL_INTERVAL) / d + offsety)
					.lineTo(this.canvas.width - offsetx, (i * SMALL_INTERVAL) / d + offsety)
					.stroke()
					.closePath();
			}
			/* 绘制标注 start */
			this.ctx
				.beginPath()
				.lineWidth((3 * multiple) / d) // 线宽
				.strokeStyle("#000000") // 颜色取值
				.moveTo((0 * 10 * multiple) / d + offsetx + 5, (5 * 10 * multiple) / d + offsety)
				.lineTo((1 * 10 * multiple) / d + offsetx + 5, (5 * 10 * multiple) / d + offsety) // 先向右画1单位的横线
				.lineTo((1 * 10 * multiple) / d + offsetx + 5, (2 * 10 * multiple) / d + offsety) // 再向上画8单位的竖线
				.lineTo((4 * 10 * multiple) / d + offsetx + 5, (2 * 10 * multiple) / d + offsety) // 再向右画3单位的横线
				.lineTo((4 * 10 * multiple) / d + offsetx + 5, (5 * 10 * multiple) / d + offsety) // 再向下画8单位的竖线
				.lineTo((5 * 10 * multiple) / d + offsetx + 5, (5 * 10 * multiple) / d + offsety) // 再向右画1单位的横线
				.stroke()
				.closePath();
			/* 绘制标注 end */

			//绘制标尺
			for (let i = 0, len = 25 * this.bigGridX; i <= len; i += 25) {
				this.ctx.textAlign("center");
				this.ctx.font(multiple * 20 + "px sans-serif");
				this.ctx.lineWidth((2 * multiple) / d); // 线宽
				this.ctx.fillText(i / 25 + "s", (i * SMALL_INTERVAL) / d + offsetx, this.canvas.height - 10);
			}
			this.curveChart(data);
		},

		//绘制心电图

		curveChart(data, dpx) {
			data = this.ecgArr;
			//console.log("data", data, textData);
			let len = 1;
			let indicate = (this.bigGridY - len) % 2 === 0 ? 25 / 2 : 0;
			let startX = offsetx,
				startY = 25 * SMALL_INTERVAL + offsety;
			let temp = startY;
			if (data.length == 0) {
				return;
			}
			// startY = temp - data[0] * 400;
			// for (let i = 0; i < data.length; i++) {
			// 	if (data[i] === null) {
			// 	} else {
			// 		this.ctx
			// 			.beginPath()
			// 			.strokeStyle("#ff0000")
			// 			.lineWidth(3 * multiple)
			// 			.moveTo(startX, startY)
			// 			.lineTo(startX + 1, temp - data[i] * 400)
			// 			.stroke()
			// 			.closePath();
			// 	}
			// 	startX = startX + 1;
			// 	startY = temp - data[i] * 400;
			// }
			this.curveChart4();
		},
		curveChart2(data, dpx) {
			if (data.length < 2) return;

			let startX = offsetx;
			let tempY = 25 * SMALL_INTERVAL + offsety;
			let prevIndex = -1;
			let prevY = null;

			this.ctx.beginPath();
			this.ctx.strokeStyle("#000000");
			this.ctx.lineWidth(3 * multiple);

			for (let i = 1; i < data.length; i++) {
				if (data[i] === null || data[i - 1] === null) {
					// 如果当前点或前一个点为 null，结束当前段
					prevY = null;
					continue;
				}

				const yPrev = tempY - data[i - 1] * multiple * 100;
				const yCurr = tempY - data[i] * multiple * 100;

				if (prevY === null) {
					// 新段开始
					this.ctx.moveTo(startX + (i - 1) * 2, yPrev);
				} else {
					// 绘制贝塞尔曲线段
					const cpx = ((i + i - 1) * 2) / 2 + offsetx;
					const cpy = (yCurr + yPrev) / 2;
					this.ctx.quadraticCurveTo((i - 1) * 2 + offsetx, yPrev, cpx, cpy);
				}

				// 更新上一个有效 Y 值
				prevY = yCurr;
			}

			this.ctx.stroke();
			this.ctx.closePath();
		},
		curveChart3(data, dpx) {
			data = this.ecgArr;
			if (data.length < 2) return;

			let startX = offsetx;
			let startY = 0;
			let tempY = 25 * SMALL_INTERVAL + offsety;

			this.ctx.beginPath();
			this.ctx.strokeStyle("#000000");
			this.ctx.lineWidth(3 * multiple);

			// 移动到第一个点
			this.ctx.moveTo(startX, tempY - data[0] * 400);
			for (let i = 1; i < data.length; i++) {
				const p0 = i === 1 ? data[0] : data[i - 1];
				const p1 = data[i];
				const p2 = data[i + 1];
				const p3 = i === data.length - 2 ? data[data.length - 1] : data[i + 2];
				const cp1x = i * 2 + ((i + 2 - (i - 1)) * 2) / 5 + offsetx;
				const cp1y = tempY - data[i] * 400 + (tempY - data[i + 1] * 400 - (tempY - data[i - 1] * 400)) / 5;
				const cp2x = (i + 1) * 2 - ((i + 2 - i) * 2) / 5 + offsetx;
				const cp2y = tempY - data[i + 1] * 400 - (tempY - data[i + 2] * 400 - (tempY - data[i] * 400)) / 5;
				this.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, (i + 1) * 2 + offsetx, tempY - data[i + 1] * 400);
			}

			this.ctx.stroke();
			this.ctx.closePath();
		},

		// curveChart4: 对心电数据进行滤波（滑动平均）并绘制
		curveChart4(data, dpx) {
			// 1. 获取数据
			data = this.ecgArr;
			if (!data || data.length < 2) return;

			// 2. 滤波：滑动平均（窗口5）
			const windowSize = 5;
			const half = Math.floor(windowSize / 2);
			const smoothData = [];
			for (let i = 0; i < data.length; i++) {
				if (data[i] === null) {
					smoothData.push(null);
				} else {
					let sum = 0,
						count = 0;
					for (let j = -half; j <= half; j++) {
						let idx = i + j;
						if (idx >= 0 && idx < data.length && data[idx] !== null) {
							sum += Number(data[idx]);
							count++;
						}
					}
					smoothData.push(count > 0 ? sum / count : data[i]);
				}
			}

			// 3. 绘制平滑后的心电曲线（折线）
			// let startX = offsetx;
			// let tempY = 25 * SMALL_INTERVAL + offsety;
			// let prevY = tempY - smoothData[0] * 400;
			// for (let i = 1; i < smoothData.length; i++) {
			// 	if (smoothData[i] === null) continue;
			// 	this.ctx
			// 		.beginPath()
			// 		.strokeStyle("#ff0000")
			// 		.lineWidth(3 * multiple)
			// 		.moveTo(startX + (i - 1), prevY)
			// 		.lineTo(startX + i, tempY - smoothData[i] * 400)
			// 		.stroke()
			// 		.closePath();
			// 	prevY = tempY - smoothData[i] * 400;
			// }
			// 4. 可选：也可以用贝塞尔曲线平滑连接
			//this.drawBezierCurve(smoothData, dpx);
			this.curveChart2(smoothData, dpx);
		},
		drawBezierCurve(data, dpx) {
			let startX = offsetx;
			let startY = 0;
			let tempY = 25 * SMALL_INTERVAL + offsety;
			let points = data.map((item, index) => {
				return {
					x: index + offsetx,
					y: tempY - item * 400
				};
			});
			let len = points.length;
			if (len < 2) {
				return;
			}
			this.ctx.beginPath();
			this.ctx.strokeStyle("#000000");
			this.ctx.lineWidth(3 * multiple);
			this.ctx.moveTo(points[0].x, points[0].y);
			if (len === 2) {
				this.ctx.lineTo(points[1].x, points[1].y);
			} else {
				let controlPoint = [];
				for (let i = 1; i < len - 1; i++) {
					controlPoint = controlPoint.concat(getControlpoint([points[i - 1], points[i], points[i + 1]]));
				}
				let i = 2;
				this.ctx.quadraticCurveTo(controlPoint[0].x, controlPoint[0].y, points[1].x, points[1].y);
				for (; i < len - 1; i++) {
					this.ctx.bezierCurveTo(
						controlPoint[(i - 2) * 2 + 1].x,
						controlPoint[(i - 2) * 2 + 1].y,
						controlPoint[(i - 1) * 2].x,
						controlPoint[(i - 1) * 2].y,
						points[i].x,
						points[i].y
					);
					// this.ctx.lineTo(points[i].x, points[i].y);
				}
				console.log(controlPoint, i);
				this.ctx.quadraticCurveTo(
					controlPoint[(i - 2) * 2 + 1].x,
					controlPoint[(i - 2) * 2 + 1].y,
					points[i].x,
					points[i].y
				);
			}

			this.ctx.stroke();
			this.ctx.closePath();
		}
	}
};
</script>
<style></style>
